/*
 * Copyright (c) 2009-2010, okazoh_tk. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *  - Neither the name of the copyright holder nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef ALKES_TREF_COUNT_H_INCLUDED_
#define ALKES_TREF_COUNT_H_INCLUDED_

#include "alkes/internal.h"
#include "alkes/core/TBitArray.h"

namespace alkes {

template<class Derived>
class TRefCount
{
public:
    void addRef();
    void release();

protected:
    TRefCount();
    ~TRefCount();

protected:
    void setFlag(uint32_t idx, bool is_on = true);
    void clearFlag(uint32_t idx);
    bool flag(uint32_t idx) const;

private:
    uint16_t count_;
    TBitArray<uint16_t> attr_;
};

template<class Derived>
TRefCount<Derived>::TRefCount()
: count_(0)
, attr_(0)
{
}

template<class Derived>
TRefCount<Derived>::~TRefCount()
{
}

template<class Derived>
void TRefCount<Derived>::addRef()
{
    ++count_;
}

template<class Derived>
void TRefCount<Derived>::release()
{
    if (count_ <= 1)
        static_cast<Derived*>(this)->destroy();
    else
        --count_;
}

template<class Derived>
void TRefCount<Derived>::setFlag(uint32_t idx, bool is_on)
{
    attr_.setFlag(idx, is_on);
}

template<class Derived>
void TRefCount<Derived>::clearFlag(uint32_t idx)
{
    attr_.clearFlag(idx);
}

template<class Derived>
bool TRefCount<Derived>::flag(uint32_t idx) const
{
    return attr_[idx];
}


template<class T>
class intrusive_ptr
{
public:
    explicit intrusive_ptr(T* ptr);
    ~intrusive_ptr();

    void operator=(T* ptr);

    T* get() const { return ptr_; }
    void copy(intrusive_ptr& rh);
    void copy(T* ptr);
    void swap(intrusive_ptr& rh);

    operator T* () { return get(); }
    operator T* () const { return get(); }

    T* operator->() { return get(); }
    const T* operator->() const { return get(); }

    T& operator*() { return *get(); }
    const T& operator*() const { return *get(); }

    T* move();

    static void* operator new(size_t /*size*/, void* buf) { return buf; }
    static void operator delete(void* /*ptr*/, void* /*buf*/) {}

private:
    intrusive_ptr(const intrusive_ptr<T>& rh);
    void operator=(const intrusive_ptr<T>& rh);

private:
    T*  ptr_;
};

template<class T>
intrusive_ptr<T>::intrusive_ptr(T* ptr)
: ptr_(ptr)
{
    if (get())
        intrusive_ptr_add_ref(get());
}

template<class T>
intrusive_ptr<T>::~intrusive_ptr()
{
    if (ptr_)
    {
        T* p = ptr_;
        ptr_ = 0;
        intrusive_ptr_release(p);
    }
}

template<class T>
void intrusive_ptr<T>::operator=(T* ptr)
{
    copy(ptr);
}

template<class T>
void intrusive_ptr<T>::copy(intrusive_ptr& rh)
{
    copy(get(), rh.get());
}

template<class T>
void intrusive_ptr<T>::copy(T* ptr)
{
    if (get() == ptr)
        return ;

    if (get())
        intrusive_ptr_release(get());

    ptr_ = ptr;

    if (get())
        intrusive_ptr_add_ref(get());
}

template<class T>
void intrusive_ptr<T>::swap(intrusive_ptr<T>& rh)
{
    T* tmp = get();
    ptr_ = rh.get();
    rh.ptr_ = tmp;
}

template<class T>
T* intrusive_ptr<T>::move()
{
    T* p = ptr_;
    ptr_ = 0;
    return p;
}

template<class T>
void intrusive_ptr_add_ref(T* p)
{
    p->addRef();
}

template<class T>
void intrusive_ptr_release(T* p)
{
    p->release();
}

}

#endif
